package webapi

import (
	"errors"
	"fmt"
	"net/http"

	"iot-base/common/common"
	"iot-base/common/dbconn"

	"github.com/gin-gonic/gin"
)

/*

原则上错误号采用jsonrpc错误号定义 https://docs.citahub.com/zh-CN/cita/rpc-guide/rpc-error-code
返回格式遵循jsonrpc的返回格式
{
     "result" : "Hell JSON-RPC",
     "error" : null,
     "id" : 1
}
返回成功时返回值在result中，错误信息在error中：
{
    "code" : 1,
    "message" : "Nothing found",
    "data":null
}
没有error也没有result视为返回成功，但是无数据返回

*/

//InitRestfulSvr 初始化api
func InitRestfulSvr(port int, prefix string) (*gin.RouterGroup, *gin.Engine) {
	router := gin.Default()

	api := router.Group("/api/" + prefix)
	router.Run(fmt.Sprintf(":%d", port))
	return api, router
}

//SendOK 返回成功
func SendOK(c *gin.Context) {
	SendResultEx("OK", nil, 0, c)
}

//ErrorStruct 错误结构
type ErrorStruct struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

//SendText 发送text
func SendText(result string, c *gin.Context) {
	c.String(http.StatusOK, `{"result":`+result+`}`)
}

//SendRawData 发送rawdata
func SendRawData(rawdata string, c *gin.Context) {
	c.String(http.StatusOK, rawdata)
}

//SendResultEx 返回结果
func SendResultEx(result interface{}, err error, errCode int, c *gin.Context) {
	if err != nil || errCode != 0 {
		if err == nil {
			err = commonErr
		}
		errstruct := ErrorStruct{
			Code:    errCode,
			Message: err.Error(),
		}
		c.JSON(http.StatusOK, gin.H{
			"error": errstruct,
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			"result": result,
		})
	}
}

//ExecError 运行命令错误
const ExecError = -320000

//RedisError redis错误
const RedisError = -330000

//DBError 数据库错误
const DBError = -330001

//ParamMissed 参数丢失
const ParamMissed = -330002

//ProxyError 代理服务错误
const ProxyError = -330003

//ParamsError 传入参数错误
const ParamsError = -330005

//ExtendError 扩展模块发生错误
const ExtendError = -340000

//SendResult 返回结果
func SendResult(result interface{}, err error, c *gin.Context) {
	if err != nil {
		SendResultEx(result, err, -1, c)
	} else {
		SendResultEx(result, err, 0, c)
	}

}

//SendError 发送错误
func SendError(err string, errCode int, c *gin.Context) {
	if errCode == 0 {
		SendResultEx(nil, nil, 0, c)
	} else {
		errHappended := errors.New(err)
		SendResultEx(nil, errHappended, errCode, c)
	}
}

var commonErr error

func init() {
	commonErr = errors.New("error")
}

//SendErrCode 发送错误号
func SendErrCode(errCode int, c *gin.Context) {
	if errCode != 0 {
		SendResultEx(nil, commonErr, errCode, c)
	} else {
		SendOK(c)
	}

}

//FUNMAP 函数绑定
type FUNMAP = map[string]func(c *gin.Context) bool

//FUNAFTERMAP 绑定
type FUNAFTERMAP = map[string]func(obj interface{}, c *gin.Context) bool

//如果返回true，则处理完成
func run(id string, funMap FUNMAP, c *gin.Context) bool {
	if funMap != nil {
		fun, ok := funMap[id]
		if ok && fun != nil {
			return fun(c)
			//return true
		}
	}
	return false
}

func runAfter(id string, afterMap FUNAFTERMAP, obj interface{}, c *gin.Context) bool {
	if afterMap != nil {
		fun, ok := afterMap[id]
		if ok && fun != nil {
			return fun(obj, c)
		}
	}
	return false
}

//BindCrudEx 绑定
func BindCrudEx(api *gin.RouterGroup, prefix string, obj dbconn.DBObjIDer, funMap FUNMAP,
	afterMap FUNAFTERMAP) *gin.RouterGroup {
	crudAPI := api.Group(prefix)
	{
		crudAPI.GET("/", func(c *gin.Context) {
			if run("GETALL", funMap, c) {
				return
			}
			res, err := dbconn.FindAll(obj)
			if err != nil {
				SendError(err.Error(), DBError, c)
				return
			}
			SendResult(res, err, c)
		})
		crudAPI.GET("/:id", func(c *gin.Context) {
			if run("GETBYID", funMap, c) {
				return
			}
			id := c.Param("id")
			if queryByCondition(id, obj, c) {
				return
			}
			//fmt.Println("query-----------by id" + id)
			res, err := dbconn.FindByID(obj, id)
			if err != nil {
				SendError(err.Error(), DBError, c)
				return
			}
			SendResult(res, err, c)
		})
		crudAPI.DELETE("/:id", func(c *gin.Context) {
			if run("DELETE", funMap, c) {
				return
			}
			id := c.Param("id")
			if id == "ALL" {
				err := dbconn.DeleteAll(obj)
				if err != nil {
					SendError(err.Error(), DBError, c)
				} else {
					SendOK(c)
				}
				return

			}
			err := dbconn.Delete(obj, id)
			if err != nil {
				SendError(err.Error(), DBError, c)
				return
			}
			if runAfter("DELETE", afterMap, id, c) {
				return
			}
			SendResult(nil, err, c)
		})

		crudAPI.PUT("/", func(c *gin.Context) {
			//id := c.Query("id")
			if run("PUT", funMap, c) {
				return
			}
			tobj, _ := common.CreateInstance(obj)

			//json := make(map[string]interface{}) //注意该结构接受的内容
			errJson := c.ShouldBind(tobj)
			if errJson != nil {
				fmt.Println("error-----------json", errJson)
			}
			tidobj, _ := tobj.(dbconn.DBObjIDer)
			//fmt.Println("put -------------", tobj)

			err := dbconn.Update(tidobj)
			if err != nil {
				SendError(err.Error(), DBError, c)
			} else {
				if runAfter("PUT", afterMap, tobj, c) {
					return
				}
				SendResult(tobj, nil, c)
			}

		})

		crudAPI.POST("/", func(c *gin.Context) {
			//id := c.Query("id")
			if run("POST", funMap, c) {
				return
			}
			tobj, _ := common.CreateInstance(obj)

			//json := make(map[string]interface{}) //注意该结构接受的内容
			errJson := c.ShouldBind(tobj)
			if errJson != nil {
				fmt.Println("error-----------json", errJson)
			}
			tidobj, _ := tobj.(dbconn.DBObjIDer)
			err := dbconn.NewItem(tidobj)
			if err != nil {
				SendError(err.Error(), DBError, c)
			} else {

				if runAfter("POST", afterMap, tobj, c) {
					return
				}
				SendResult(tobj, nil, c)
			}

		})

	}
	return crudAPI
}

//BindCrud 绑定
func BindCrud(api *gin.RouterGroup, prefix string, obj dbconn.DBObjIDer) *gin.RouterGroup {
	return BindCrudEx(api, prefix, obj, nil, nil)
}

//Cors 跨域
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		if c.Request.URL == nil || len(method) < 3 {
			return
		}
		//method := c.Request.Method               //请求方法
		origin := c.Request.Header.Get("Origin") //请求头部

		if origin != "" {
			c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
			c.Request.Header.Del("Origin")
		} else {
			c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		}
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,api_token")
		//c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With")

		c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,api_token")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
		c.Header("Access-Control-Max-Age", "172800")                                                                                                                                                           // 缓存请求信息 单位为秒
		//c.Header("Access-Control-Allow-Credentials", "false")                                                                                                                                                  //  跨域请求是否需要带cookie信息 默认设置为true
		c.Set("content-type", "application/json") // 设置默认返回格式是json
		if c.Request.Method == "OPTIONS" {
			c.JSON(http.StatusOK, "Options Request!")
			return
		}

		//放行所有OPTIONS方法 原因为当Request Header中存在自定义的key时，浏览器会首先发送一个options请求，判断后端是否支持，只有返回状态码为200时，才会真正发送请求
		// 处理请求
		c.Next()
	}
}
